/*
 * assignment.h
 *
 *  Created on: Jun 3, 2011
 *      Author: tqlong
 */

#ifndef ASSIGNMENT_H_
#define ASSIGNMENT_H_

/** An assignment of values to the argument of a function*/
class Assignment : protected std::vector<int>
{
  typedef std::vector<int> __Base;
  std::vector<bool> fixed_;
public:
  Assignment(int n_var) :
    __Base(n_var, 0)
  {
  }

  Assignment()
  {
  }

  int
  n_var() const
  {
    return size();
  }

  int
  operator[](int i) const
  {
    return this->at(i);
  }

  int&
  operator[](int i)
  {
    return this->at(i);
  }

  /** fixed an assigment to a variable */
  void
  setFixed(int i, bool fixed = true)
  {
    if (fixed_.size() == 0) fixed_.resize(n_var(), false);
    fixed_[i] = fixed;
  }

  /** check if a variable is fixed */
  bool
  isFixed(int i) const
  {
    return ((int) fixed_.size() == n_var() && fixed_[i]);
  }

  /** check valid assignment */
  bool
  isValid() const
  {
    return (n_var() > 0 && at(0) >= 0);
  }

  /** fixed an assignment according to constant variable in a universe */
  Assignment
  fixed(const Argument& arg, const Universe& u)
  {
    Assignment a = *this;
    for (int i = 0; i < arg.n_var(); i++)
      {
        int value = u.value(arg.varIdx(i));
        if (value >= 0)
          {
            a.setFixed(i, true);
            a[i] = value;
          }
        else
          {
            a.setFixed(i, false);
          }
      }
    return a;
  }

  /** generate next compatible assignment (after fixed) with an universe */
  bool
  nextAssignment(const Argument& arg, const Universe& u)
  {
    if (n_var() != arg.n_var())
      {
        std::cerr << "Number of variables mismatch at " << __FILE__ << ":"
            << __LINE__ << std::endl;
        exit(1);
      }
    for (int i = 0; i < n_var(); i++)
      {
        if (isFixed(i)) continue;
        if (at(i) < u.cardinality(arg.varIdx(i)) - 1)
          {
            at(i)++;
            return true;
          }
        else
          {
            at(i) = 0;
          }
      }
    if (n_var() > 0) at(0) = -1; // make the assignment invalid
    return false;
  }

  Assignment
  reducedAssignment() const
  {
    int n = 0;
    for (int i = 0; i < n_var(); i++)
      if (!isFixed(i)) n++;
    //std::cout << "n_var=" << n_var << std::endl;
    Assignment a_reduced(n);
    int j = 0;
    for (int i = 0; i < n_var(); i++)
      if (!isFixed(i))
        {
          //std::cout << j << " " << i << " a[i]=" << a[i] << std::endl;
          a_reduced[j++] = at(i);
        }
    //std::cout << "rd="; a_reduced.print(); std::cout << std::endl;
    return a_reduced;
  }

};

struct AssignmentCompare
{
  bool
  operator()(const Assignment& lhs, const Assignment& rhs) const
  {
    if (lhs.n_var() != rhs.n_var())
      {
        std::cerr << "Number of variables mismatch at " << __FILE__ << ":"
            << __LINE__ << std::endl;
        exit(1);
      }

    int i;
    for (i = 0; i < lhs.n_var() && i < rhs.n_var(); i++)
      if (lhs[i] < rhs[i])
        return true;
      else if (lhs[i] > rhs[i]) return false;
    return false;
  }
};

template<typename OS>
  OS&
  operator<<(OS& os, const Assignment& a)
  {
    if (a.n_var() == 0) return os;
    os << a[0];
    for (int i = 1; i < a.n_var(); i++)
      os << "," << a[i];
    return os;
  }

#endif /* ASSIGNMENT_H_ */
